/*	
*	File: igmpv2-header.h
*	Date: 2011/11/25
*	User: Ronmaru
*	Desctiption: igmp versin 2 header
*/


#ifndef IGMPV4_HEADER_H
#define IGMPV4_HEADER_H

#include <istream>
#include <ostream>
#include <algorithm>
#include <boost/asio/ip/address_v4.hpp>

// IGMPv2 header
//
// The wire format of an IGMPv2 header is:
// 
// 0               8               16                             31
// +---------------+---------------+------------------------------+      ---
// |               |               |                              |       ^
// |     type      | Max Resp Time |          checksum            |       |
// |               |               |                              |       |
// +---------------+---------------+------------------------------+    8 bytes
// |                                                              |       |
// |                         Group Address                        |       |
// |                                                              |       v
// +-------------------------------+------------------------------+      ---

const int IGMPV4_MAX_HEADER_LENGTH = 8;

class IGMPv2header
{
public:
  IGMPv2header() { std::fill(rep_, rep_ + sizeof(rep_), 0); }

  void SetType(unsigned char n) { rep_[0] = n; }
  void SetMaxRespTime(unsigned char n) { rep_[1] = n; }
  void SetChecksum(unsigned short n) { Encode(2, 3, n); }
  void SetGroupAddress(boost::asio::ip::address_v4 address) { 
    boost::asio::ip::address_v4::bytes_type bytes = address.to_bytes();
    rep_[4] = bytes[0];
    rep_[5] = bytes[1];
    rep_[6] = bytes[2];
    rep_[7] = bytes[3];
  }

  unsigned char GetType() const { return rep_[0]; }
  unsigned char GetMaxRespTime() const { return rep_[1]; }
  unsigned short GetChecksum() const { return Decode(2, 3); }
  boost::asio::ip::address_v4 GetGroupAddress() const
  {
    boost::asio::ip::address_v4::bytes_type bytes
      = { { rep_[4], rep_[5], rep_[6], rep_[7] } };
    return boost::asio::ip::address_v4(bytes);
  }

  friend std::istream& operator>>(std::istream& is, IGMPv2header& header)
  { return is.read(reinterpret_cast<char*>(header.rep_), IGMPV4_MAX_HEADER_LENGTH); }

  friend std::ostream& operator<<(std::ostream& os, const IGMPv2header& header)
  { return os.write(reinterpret_cast<const char*>(header.rep_), IGMPV4_MAX_HEADER_LENGTH); }

  void ComputeChecksum() 
  {
    SetChecksum(0);
    unsigned int sum = 0;

    for (int i = 0; i < IGMPV4_MAX_HEADER_LENGTH; ++i) {
      sum += rep_[i];
    }

    sum = (sum >> 16) + (sum & 0xFFFF);
    sum += (sum >> 16);
    SetChecksum(sum);
  }

private:
  unsigned short Decode(int a, int b) const
  { return (rep_[a] << 8) + rep_[b]; }

  void Encode(int a, int b, unsigned short n)
  {
    rep_[a] = static_cast<unsigned char>(n >> 8);
    rep_[b] = static_cast<unsigned char>(n & 0xFF);
  }

  unsigned char rep_[IGMPV4_MAX_HEADER_LENGTH];
};

#endif // IGMPV4_HEADER_H